<?php

namespace App\Console\Commands;


use App\Common\RedisCache\AddedUserVideoByTagCache;
use App\Common\RedisCache\AddVideoByTagCache;
use App\Common\RedisCache\LiveRoomRobotSortedSetCache;
use App\Common\RedisCache\UserFocusSetCache;
use App\Common\RedisCache\UserPraiseVideoSetCache;
use App\Common\RedisCache\UsersFocusChangeDataListCache;
use App\Common\Utill\ElasticSearch;
use App\Model\Focus;
use App\Model\Video\TagCategory;
use App\Model\Video\Video;
use App\Model\Video\VideoDetails;
use App\Model\Video\VideoTags;
use App\Model\VideoPraised;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;


/**
 * test
 */
class AddVideoByTagBulk extends Command
{
    //*/5 * * * * php /data/video_background/artisan AddVideoByTagBulk exec >> /dev/null 2>&1
    //php artisan AddVideoByTagBulk process --robot_counter=50001 --insert_counter=3000
    protected $signature = 'AddVideoByTagBulk {option} {--process_num=1} {--robot_counter=50000} {--insert_counter=3000} {--current_process=1}';#你的方法名字
    static $type = [
        '0' => 5,
        '1' => 50,
        '2' => 100,
    ];
    protected $counter = 0;
    protected $readLimit = 10000;
    /**
     * The console command description.
     * @var string
     */
    protected $description = '通过视频标签给用户加视频';
    public $sqlConnection = 'star_rocks_mysql';

    /**
     * Create a new command instance.
     * @return void
     */
    protected function configure()
    {
        $this->setName('AddVideoByTagBulk')->setDescription('通过视频标签给用户加视频');
    }


    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }
    /**
     * @return int|void
     */
    public function handle()
    {
        $option = $this->argument('option');
        switch ($option) {
            case 'process':
                $processNum = $this->option('process_num');
                $insertCounter = $this->option('insert_counter');
                $robotCounter = $this->option('robot_counter');
                $this->process($processNum, $robotCounter, $insertCounter);
                break;
            case 'exec':
                $this->worker();
                break;
            case 'clear':
                $this->clearData();
                break;
        }
    }

    /**
     * 多进程
     * todo 测试一个进程占rss内存125M  开多个进程需考虑机器物理phyMem内存
     */
    private function process($processNum, $robotCounter, $insertCounter)
    {
        for ($i = 1; $i <= $processNum; $i++) {
//            $shell = vsprintf('php artisan AddVideoByTagBulk exec --process_num %s --robot_counter %s --insert_counter %s --current_process %s >> /tmp/aa.txt 2>&1', [$processNum, $robotCounter, $insertCounter, $i]);
            $shell = vsprintf('php artisan AddVideoByTagBulk exec --process_num %s --robot_counter %s --insert_counter %s --current_process %s >> /dev/null 2>&1', [$processNum, $robotCounter, $insertCounter, $i]) . ' &';
            exec($shell);
        }
    }
    /**
     * 执行
     */
    public function worker()
    {
//        $robot = new LiveRoomRobotSortedSetCache();
//        $robot->setKeyParameter(['prefix' => 'AllRobotUser']);
//        $j = 0;
//        for ($i=1000;$i<51000;$i++) {
//            $arr[$i] = $j++;
//        }
//        dd($robot->set($arr));
        $sTime = microtime(true);
        Log::info($this->description . '正在处理中...');
        ini_set('memory_limit', '512M');
        set_time_limit(0);
        //机器人总数量
        $this->counter = $this->option('robot_counter');
        $counter = $this->counter;
        //insert最大数量 //client default of 32m server is 4m
        $insertCounter = $this->option('insert_counter');
        $currentProcess = $this->option('current_process');
        $processNum = $this->option('process_num');
        $usersFocusChangeDataListCache = new UsersFocusChangeDataListCache();
        $usersFocusChangeDataListCache->setKeyParameter();
        $addedRobotUser = new AddedUserVideoByTagCache();
        $addedRobotUser->setKeyParameter(['addedUserId']);
        $robot = new LiveRoomRobotSortedSetCache();
        $robot->setKeyParameter(['prefix' => 'AllRobotUser']);
        $addVideoByTagCache = new AddVideoByTagCache();
        $addVideoByTagCache->setKeyParameter(['addedVideoId']);
        $perPage = $counter/$processNum;
        $robotData = $robot->getSortedSetList($currentProcess, $perPage);
        $userPraiseVideoSetCache = new UserPraiseVideoSetCache();
        $serFocusSetCache = new UserFocusSetCache();
        $runningCounter = 0;//sql写io轮数
        $timerCounter = $start = 0;//顺序写总次数
        //最新一条视频ID
        $newVideoId = Video::query()->orderBy('id','desc')->limit(1)->first()->id + 100;
        $startVideoId = $newVideoId;
        $robotCounter = count($robotData);
        while ($counter > 0) {
            $writeVideoData = $writePraiseData = $writeFocusData = $addedVideoId = $addedUserId = $videoDetailData = $fansAndFocusData = [];
            $timer = 0;
            $praiseRandRes = [];
            Log::info($this->description.'机器人总数---'.$robotCounter);
            for ($j = $start; $j < $robotCounter; $j++) {
                if (!isset($robotData[$j])) {
                    dd(1);
                    break;
                }
                $userId = $robotData[$j];
                //避免用户重复添加
                if ($addedRobotUser->memberExists($userId)) {
                    continue;
                }
                //获取随机视频列表
                $addVideoLists = $this->getRandVideoList();
                if (empty($addVideoLists)) {
                    Log::info($this->description.'没数据了$addVideoLists');
                    goto writeDone;
                }

                //拼装视频等其他数据
                $praiseCount = rand(1,100);
                if (!isset($praiseRandRes[$j])) {
                    $praiseRandRes[$j] = $this->getPraiseRand($praiseCount,count($addVideoLists));
                }

                foreach ($addVideoLists as $k => &$addVideoList) {
                    unset($addVideoList['merchant_id']);
                    $videoId = ++$newVideoId;
                    $date = date('Y-m-d H:i:s');
                    $addVideoList['id'] = $videoId;
                    $addVideoList['user_id'] = $userId;
                    $addVideoList['created_at'] = $date;
                    $addVideoList['updated_at'] = $date;
                    $writeVideoData[] = $addVideoList;
                    if (in_array($videoId,$addedVideoId)) {
                        dd($videoId);
                    }
                    $addedVideoId[] = $videoId;
                    $addedUserId[] = $userId;

                    // 更新用户点赞视频缓存
                    $userPraiseVideoSetCache->setKeyParameter([$userId]);
                    $userPraiseVideoSetCache->addVal($videoId);
                    //videoDetail表组织结构
                    $videoDetailData[] = [
                        'video_size'  => 0,//视频大小
                        'video_time'  => 0,//视频时长
                        'hot'         => self::$type[$addVideoList['type']],//hot
                        'initial_hot' => self::$type[$addVideoList['type']],//初始hot
                        'created_at'  => $date,
                        'video_id'    => $videoId,//视频id
                        'praised_count' => $praiseRandRes[$j][$k],//点赞量
                    ];
                    //处理点赞只执行一次
                    if ($k) continue;
                    $praiseTempRobotData = $robotData;
                    for ($i = 0; $i < $praiseCount; $i++) {
                        $randPraiseIndex = array_rand($praiseTempRobotData);
                        $praiseUserId = $praiseTempRobotData[$randPraiseIndex];
                        if ($praiseUserId == $userId) continue;
                        //点赞数据
                        $writePraiseData[] = [
                            'user_id' => $praiseUserId,//点赞者ID
                            'video_id' => $videoId,
                            'video_user_id' => $userId,
                        ];
                        //本轮限制点赞UNIQUE
                        unset($praiseTempRobotData[$randPraiseIndex]);
                        $timer++;
                    }
                }
                unset($addVideoList);
                //处理关注
                $focusRand = rand(5,10);
                $fansRand = rand(2,6);
                $focusRandTempRobotData = $robotData;
                for ($i = 0; $i < $focusRand; $i++) {
                    $focusRandIndex = array_rand($focusRandTempRobotData);
                    $focusUserId = $focusRandTempRobotData[$focusRandIndex];
                    if ($focusUserId == $userId) continue;
                    //本轮限制关注UNIQUE
                    unset($focusRandTempRobotData[$focusRandIndex]);
                    $timer++;
                    //关注数据
                    $writeFocusData[] = [
                        'user_id' => $userId,
                        'focus_user_id' => $focusUserId,//关注者ID
                    ];
                    //粉丝数
                    for ($f = 0; $f < $fansRand; $f++) {
                        $focusUserId = $robotData[array_rand($robotData)];
                        $serFocusSetCache->setKeyParameter([$userId]);
                        $serFocusSetCache->addVal($focusUserId);
                        $focusDataCache['type'] = 1;
                        //用户粉丝量数据变化
                        $focusDataCache['focusUserId']    = $focusUserId;
                        $usersFocusChangeDataListCache->set($focusDataCache);
                        $timer++;
                    }
                }
                //关注量数据
                $fansAndFocusData[] = [
                    'userId' => $userId,
                    'focusRand'  => $focusRand,
                    'fansRand'  => $focusRand*$fansRand,
                    'praisedRand' => $praiseCount
                ];
                if (
                    count($writeVideoData) >= $insertCounter
                    || count($writePraiseData) >= $insertCounter
                    || count($writeFocusData) >= $insertCounter
                ) {
                    goto writeData;
                }
            }
            if ($timer == 0) {
                Log::info($this->description.'--timer等于00000--$j'.$j);
                goto writeDone;
            }
            writeData:
            $runningCounter++;
            $timerCounter += $timer;
            $counter = $counter - ($j-$start);
            $start = $j;

            //写video数据
            $this->addVideo($writeVideoData, $videoDetailData, $addedUserId);
            //写video-redis数据
            $addVideoByTagCache->set($addedVideoId);
            $addedRobotUser->set($addedUserId);
            //写praise数据
            VideoPraised::query()->insert($writePraiseData);
//            写focus数据
            Focus::query()->insert($writeFocusData);
            foreach ($fansAndFocusData as $v) {
                DB::table('users_info')->where('user_id', $v['userId'])->update([
                    'praise_num' => $v['praisedRand'],
                    'fans_num' => $v['fansRand'],
                    'focus_num' => $v['focusRand'],
                ]);
            }
            Log::info($this->description . '-写次数--'.$timer);
            Log::info($this->description.'--处理完成的机器人用户数量--'.$j);
            Log::info($this->description.'--还剩机器人用户数量--'.$counter);

        }
        writeDone:
        Log::info($this->description . '-数据库写操作轮数--'.$runningCounter);
        $eTime = microtime(true);
        Log::info($this->description . '-总耗时--' . ($sTime - $eTime) . '秒');
        Log::info($this->description . '-写总次数--'.$timerCounter);
        Log::info($this->description . '-初始的视频ID--'.$startVideoId);
        Log::info($this->description . '-最新的视频ID--'.$newVideoId);
    }

    /**
     * 获取随机视频
     */
    public function getRandVideoList()
    {
        global $newTagAndVideo;//最终视频和标签数据
        global $tags;//一次的标签数据
        global $categoryNameArr;//所有分类数据递减
        randVideoResult:
        //一直从$newTagAndVideo中随机拿视频，直到是[]
        if (!empty($newTagAndVideo)) {
            $randTag = $tags[array_rand($tags)];
            $rangC = count($newTagAndVideo[$randTag]);
            $videoList = array_rand($newTagAndVideo[$randTag], rand(1, min($rangC, 10)));
            $videoList = (!is_array($videoList)) ? [$videoList] : $videoList;

            $result = [];
            foreach ($videoList as $v) {
                $result[] = $newTagAndVideo[$randTag][$v];
                unset($newTagAndVideo[$randTag][$v]);
                if (empty($newTagAndVideo[$randTag])) {
                    unset($newTagAndVideo[$randTag]);
                    unset($tags[array_search($randTag, $tags)]);
                }
            }
            return $result;
        }
        if (empty($categoryNameArr) || count($categoryNameArr) < 150) {
            //获取所有三级标签
            $cateGoryList = (new TagCategory())->setConnection($this->sqlConnection)->setTable('ex_tag_category')
                ->where(['level' => 3, 'is_del' => 0])->get()->toArray();
            if (empty($cateGoryList)) {
                Log::info($this->description . '--没有运营添加的标签');
                return false;
            }
            $categoryNameArr = array_unique(array_column($cateGoryList, 'category_name'));
        }
        //从所有标签中移除随机拿的100个标签
        $categoryNameArrRand = array_rand($categoryNameArr, 100);
        $categoryNameArrWhere = [];
        foreach ($categoryNameArrRand as $categoryNameArrVal) {
            $categoryNameArrWhere[] = $categoryNameArr[$categoryNameArrVal];
        }
        //最终视频和标签数据
        $tagAndVideoList = (new VideoTags())->setConnection($this->sqlConnection)
            ->setTable('ex_video_tags')
            ->select(['tags','video_id','v.*'])
            ->whereIn('tags', $categoryNameArrWhere)
            ->where('is_del', 0)
            ->leftJoin('ex_video as v','video_id','=','v.id')
            ->where(['is_delete' => 2, 'state' => 1])
            ->limit($this->readLimit)
            ->get()->toArray();

        if (empty($tagAndVideoList)) {
            Log::info($this->description . '--没有运营添加的视频');
            return false;
        }
        //组装数据['标签'=>[视频列表]]
        $tags = [];
        foreach ($tagAndVideoList as &$tagAndVideo) {
            $tag = $tagAndVideo['tags'];
            if (!in_array($tag, $tags)) {
                $tags[] = $tag;
            }
            unset($tagAndVideo['tags']);
            unset($tagAndVideo['video_id']);
            $newTagAndVideo[$tag][] = $tagAndVideo;
        }
        goto randVideoResult;
    }


    /**
     * 视频批量添加
     * @param $writeVideoData
     * @param $videoDetailData
     * @param $addedUserId
     * @return void
     * @throws \Throwable
     */
    public function addVideo($writeVideoData, $videoDetailData, $addedUserId):void
    {
        if (empty($writeVideoData)) return;
        try {
            DB::beginTransaction();
            Video::query()->insert($writeVideoData);
            VideoDetails::query()->insert($videoDetailData);
            DB::commit();
            foreach ($writeVideoData as &$videoInfo) {
                $videoInfo['coordinate'] = [
                    "lng" => 0,
                    "lat" => 0
                ];
            }
            (new ElasticSearch())->index('video_pool_notags')->data($writeVideoData)->bulkById('index','id');
        }catch (\Exception $exception) {
            Log::info($this->description . 'error:--'.$exception->getMessage().':'.$exception->getLine());
            $addVideoByTagCache = new AddVideoByTagCache();
            $addVideoByTagCache->setKeyParameter(['addFailedUserId']);
            $addVideoByTagCache->set($addedUserId);
            DB::rollBack();
        }
    }

//delete from ex_video_praised where video_user_id < 999 and  video_user_id > 990 or video_user_id < 999 and  video_user_id > 990
//delete from ex_focus where user_id < 999  and user_id > 990 or focus_user_id < 999 and focus_user_id > 990
//delete from ex_video where user_id  < 999 and user_id > 990
//
//
//select * from ex_video_praised where video_id = 416122

    /**
     * 清理机器人点赞、关注、粉丝数
     * @return void
     */
    public function clearData()
    {
        $robot = new LiveRoomRobotSortedSetCache();
        $robot->setKeyParameter(['prefix' => 'AllRobotUser']);
        $countRobot = 50000;
        $number = 5000;
        $times = (int)ceil($countRobot/$number);
        for ($i = 1; $i <= $times; $i++) {
            $robotData = $robot->getSortedSetList($i, $number);
            if (empty($robotData)) break;
            DB::table('users_info')->whereIn('user_id', $robotData)->update([
                'praise_num' => 0,
                'fans_num' => 0,
                'focus_num' => 0
            ]);
            VideoPraised::query()->whereIn('video_user_id', $robotData)->delete();
            Focus::query()->whereIn('focus_user_id', $robotData)->delete();
//            VideoPraised::query()->whereIn('video_user_id', $robotData)->update([
//                'is_cancel' => 1,//取消点赞
//                'is_del' => 1,//删除
//            ]);
//            Focus::query()->whereIn('focus_user_id', $robotData)->update([
//                'is_cancel' => 2,//取消关注
//                'is_del' => 1,//删除
//            ]);
        }
    }

    public function getPraiseRand($sum, $c)
    {

        $res = [];
        for ($i = 0; $i < $c; $i++) {
            $reduce = rand(ceil($sum/$c/2), ceil($sum/$c));
            if ($i == $c-1) {
                $reduce = $sum - array_sum($res);
            }
            $res[] = $reduce;
        }
        return $res;
    }

    public function simulationAlgorithm()
    {
        $res = [];
        for ($i = 0; $i < 2; $i++) {
            $sum = rand(1,100);
            $c = rand(1,10);
            if (!isset($res[$i])) {
                $res[$i] = $this->getPraiseRand($sum,$c);
            }
            $result = [];
            for ($j = 0; $j < $c; $j++) {
                $result[] = $res[$i][$j];
            }
        }
        return $result;
    }

}
